/* Tencent is pleased to support the open source community by making Hippy available.
 * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Hippy/Flex/HPNode.h"
#include <algorithm>
#include <string.h>
#include <string>

//the layout progress refers https://www.w3.org/TR/css-flexbox-1/#layout-algorithm

std::string getIndentString(int indent) {
  std::string str;
  for (int i = 0; i < indent; i++) {
    str += " ";
  }
  return str;
}

std::string toString(float value) {
  if (isUndefined(value)) {
    return "NAN";
  } else {
    char str[10] = { 0 };
    snprintf(str, 9, "%0.f", PixelRoundInt(value));
    return str;
  }
}

void HPNode::printNode(uint32_t indent) {
  std::string indentStr = getIndentString(indent);
  std::string startStr;
  startStr = indentStr
      + "<div layout=\"width:%s; height:%s; left:%s; top:%s;\" style=\"%s\">\n";
  HPLogd(startStr.c_str(), toString(result.dim[0]).c_str(),
         toString(result.dim[1]).c_str(), toString(result.position[0]).c_str(),
         toString(result.position[1]).c_str(), style.toString().c_str());

  std::vector<HPNodeRef>& items = children;
  for (size_t i = 0; i < items.size(); i++) {
    HPNodeRef item = items[i];
    item->printNode(indent + 4);
  }
  std::string endStr = indentStr + "</div>\n";
  HPLogd(endStr.c_str());
}

HPNode::HPNode() {
  context = nullptr;
  parent = nullptr;
  measure = nullptr;
  dirtiedFunc = nullptr;

  initLayoutResult();
  inInitailState = true;
}

HPNode::~HPNode() {
  //remove from parent
  if (parent != nullptr) {
    parent->removeChild(this);
    parent = nullptr;
  }

  //set child's parent as null
  for (size_t i = 0; i < children.size(); i++) {
    HPNodeRef item = children[i];
    if (item != nullptr) {
      item->setParent(nullptr);
    }
  }

  children.clear();
}

void HPNode::initLayoutResult() {
#ifdef  LAYOUT_TIME_ANALYZE
  fetchCount = 0;
#endif
  isFrozen = false;
  isDirty = true;
  _hasNewLayout = false;
  result.dim[DimWidth] = 0;
  result.dim[DimHeight] = 0;

  memset((void *) result.position, 0, sizeof(float) * 4);
  memset((void *) result.cachedPosition, 0, sizeof(float) * 4);
  memset((void *) result.margin, 0, sizeof(float) * 4);
  memset((void *) result.padding, 0, sizeof(float) * 4);
  memset((void *) result.border, 0, sizeof(float) * 4);

  result.hadOverflow = false;
  result.direction = DirectionInherit;
}

bool HPNode::reset() {
  if (childCount() != 0 || getParent() != nullptr)
    return false;
  children.clear();
  children.shrink_to_fit();
  initLayoutResult();
  inInitailState = true;
  return true;
}

void HPNode::resetLayoutRecursive(bool isDisplayNone) {
  if (inInitailState && isDisplayNone) {
    return;
  }
  initLayoutResult();
  if (!isDisplayNone) {  //see HPNode::removeChild
    //set result as undefined.see HPNodeChildTest.cpp
    //in tests/folder
    result.dim[DimWidth] = VALUE_UNDEFINED;
    result.dim[DimHeight] = VALUE_UNDEFINED;
  } else {
    inInitailState = true;	//prevent resetLayoutRecursive run many times in recursive
    //in DisplayNone state, set hasNewLayout as true;
    //set dirty false;
    setHasNewLayout(true);
    setDirty(false);
  }
  //if just because parent's display type change,
  //not to clear child layout cache, can be reused.
  layoutCache.clearCache();
  for (size_t i = 0; i < children.size(); i++) {
    HPNodeRef item = children[i];
    item->resetLayoutRecursive(isDisplayNone);
  }
}

HPStyle HPNode::getStyle() {
  return style;
}

void HPNode::setStyle(const HPStyle& st) {
  style = st;
  //TODO:: layout if needed???
}

bool HPNode::setMeasureFunc(HPMeasureFunc _measure) {
  if (measure == _measure) {
    return true;
  }

  //not leaf node , not set measure
  if (childCount() > 0) {
    return false;
  }

  measure = _measure;
  style.nodeType = _measure ? NodeTypeText : NodeTypeDefault;
  markAsDirty();
  return true;
}

void HPNode::setParent(HPNodeRef _parent) {
  parent = _parent;
}

HPNodeRef HPNode::getParent() {
  return parent;
}

void HPNode::addChild(HPNodeRef item) {
  if (item == nullptr) {
    return;
  }
  item->setParent(this);
  children.push_back(item);
  markAsDirty();
}

bool HPNode::insertChild(HPNodeRef item, uint32_t index) {
  //measure node cannot have child.
  if (item == nullptr || measure != nullptr) {
    return false;
  }
  item->setParent(this);
  children.insert(children.begin() + index, item);
  markAsDirty();
  return true;
}

HPNodeRef HPNode::getChild(uint32_t index) {
  if (index > children.size() - 1) {
    return nullptr;
  }
  return children[index];
}

bool HPNode::removeChild(HPNodeRef child) {
  std::vector<HPNodeRef>::iterator p = std::find(children.begin(),
                                                 children.end(), child);
  if (p != children.end()) {
    children.erase(p);
    child->setParent(nullptr);
    child->resetLayoutRecursive(false);
    markAsDirty();
    return true;
  }
  return false;
}

bool HPNode::removeChild(uint32_t index) {
  if (index > children.size() - 1) {
    return false;
  }
  HPNodeRef child = getChild(index);
  if (child != nullptr) {
    child->setParent(nullptr);
    child->resetLayoutRecursive(false);
  }
  children.erase(children.begin() + index);
  markAsDirty();
  return true;
}

uint32_t HPNode::childCount() {
  return children.size();
}

void HPNode::setDisplayType(DisplayType displayType) {
  if (style.displayType == displayType)
    return;
  style.displayType = displayType;
  isDirty = false;  //force following markAsDirty did effect to its parent
  markAsDirty();
}

void HPNode::markAsDirty() {
  if (!isDirty) {
    setDirty(true);
    if (parent) {
      parent->markAsDirty();
    }
  }
}

void HPNode::setHasNewLayout(bool hasNewLayoutOrNot) {
  _hasNewLayout = hasNewLayoutOrNot;
}

bool HPNode::hasNewLayout() {
  return _hasNewLayout;
}

void HPNode::setDirty(bool dirtyOrNot) {
  if (isDirty == dirtyOrNot) {
    return;
  }
  isDirty = dirtyOrNot;
  if (isDirty) {
    //reset layout direction to initial state
    //need to calculated again
    setLayoutDirection(DirectionInherit);
    //if is dirty, reset frozen.
    isFrozen = false;
    //if is dirty, layout cache muse be in clear state.
    layoutCache.clearCache();
    if (dirtiedFunc != nullptr) {
      dirtiedFunc(this);
    }
  }
}

void HPNode::setDirtiedFunc(HPDirtiedFunc _dirtiedFunc) {
  dirtiedFunc = _dirtiedFunc;
}

void HPNode::setContext(void * _context) {
  context = _context;
}

void * HPNode::getContext() {
  return context;
}

bool HPNode::isLayoutDimDefined(FlexDirection axis) {
  return isDefined(result.dim[axisDim[axis]]);
}

void HPNode::setLayoutDim(FlexDirection axis, float value) {
  result.dim[axisDim[axis]] = value;
}

inline void HPNode::setLayoutDirection(HPDirection direction) {
  result.direction = direction;
}

inline HPDirection HPNode::getLayoutDirection() {
  return result.direction;
}

float HPNode::getLayoutDim(FlexDirection axis) {
  if (!isLayoutDimDefined(axis)) {
    return VALUE_UNDEFINED;
  }
  return result.dim[axisDim[axis]];
}

float HPNode::getMainAxisDim() {
  FlexDirection mainAxis = style.flexDirection;
  if (!isLayoutDimDefined(mainAxis)) {
    return VALUE_UNDEFINED;
  }
  return result.dim[axisDim[mainAxis]];
}

float HPNode::getStartBorder(FlexDirection axis) {
  return style.getStartBorder(axis);
}

float HPNode::getEndBorder(FlexDirection axis) {
  return style.getEndBorder(axis);
}

float HPNode::getStartPaddingAndBorder(FlexDirection axis) {
  return style.getStartPadding(axis) + style.getStartBorder(axis);
}

float HPNode::getEndPaddingAndBorder(FlexDirection axis) {
  return style.getEndPadding(axis) + style.getEndBorder(axis);
}

float HPNode::getPaddingAndBorder(FlexDirection axis) {
  return getStartPaddingAndBorder(axis) + getEndPaddingAndBorder(axis);
}

float HPNode::getStartMargin(FlexDirection axis) {
  return style.getStartMargin(axis);
}

float HPNode::getEndMargin(FlexDirection axis) {
  return style.getEndMargin(axis);
}

float HPNode::getMargin(FlexDirection axis) {
  return style.getStartMargin(axis) + style.getEndMargin(axis);
}

bool HPNode::isAutoStartMargin(FlexDirection axis) {
  return style.isAutoStartMargin(axis);
}

bool HPNode::isAutoEndMargin(FlexDirection axis) {
  return style.isAutoEndMargin(axis);
}

void HPNode::setLayoutStartMargin(FlexDirection axis, float value) {
  result.margin[axisStart[axis]] = value;
}

void HPNode::setLayoutEndMargin(FlexDirection axis, float value) {
  result.margin[axisEnd[axis]] = value;
}

inline float HPNode::getLayoutMargin(FlexDirection axis) {
  return getLayoutStartMargin(axis) + getLayoutEndMargin(axis);
}

float HPNode::getLayoutStartMargin(FlexDirection axis) {
  return
  isDefined(result.margin[axisStart[axis]]) ? result.margin[axisStart[axis]] : 0;
}

float HPNode::getLayoutEndMargin(FlexDirection axis) {
  return
  isDefined(result.margin[axisEnd[axis]]) ? result.margin[axisEnd[axis]] : 0;
}

/* If both axisStart and axisEnd are defined,
 * then use axisStart. Otherwise use which is defined.
 * @param axis flex direction
 * @param forAxisStart
 * 		  true  get relative value for axis start
 * 		  false get relative value for axis end
 */
float HPNode::resolveRelativePosition(FlexDirection axis, bool forAxisStart) {
  if (style.positionType != PositionTypeRelative) {
    return 0.0f;
  }

  if (isDefined(style.getStartPosition(axis))) {
    float value = style.getStartPosition(axis);
    return forAxisStart ? value : -value;
  } else if (isDefined(style.getEndPosition(axis))) {
    float value = style.getEndPosition(axis);
    return forAxisStart ? -value : value;
  }

  return 0.0f;
}

void HPNode::setLayoutStartPosition(FlexDirection axis, float value,
                                    bool addRelativePosition) {
  if (addRelativePosition && style.positionType == PositionTypeRelative) {
    value += resolveRelativePosition(axis, true);
  }

  if (!FloatIsEqual(result.cachedPosition[axisStart[axis]], value)) {
    result.cachedPosition[axisStart[axis]] = value;
    setHasNewLayout(true);
  }

  result.position[axisStart[axis]] = value;
}

void HPNode::setLayoutEndPosition(FlexDirection axis, float value,
                                  bool addRelativePosition) {
  if (addRelativePosition && style.positionType == PositionTypeRelative) {
    value += resolveRelativePosition(axis, false);
  }

  if (!FloatIsEqual(result.cachedPosition[axisEnd[axis]], value)) {
    result.cachedPosition[axisEnd[axis]] = value;
    setHasNewLayout(true);
  }

  result.position[axisEnd[axis]] = value;
}

float HPNode::getLayoutStartPosition(FlexDirection axis) {
  return result.position[axisStart[axis]];
}

float HPNode::getLayoutEndPosition(FlexDirection axis) {
  return result.position[axisEnd[axis]];
}

//calculate main axis by refer this node's flex direction
//and layout direction which resolved in resolveDirection.
FlexDirection HPNode::resolveMainAxis() {
  FlexDirection mainAxis = style.flexDirection;
  HPDirection direction = getLayoutDirection();
  if (direction == DirectionRTL) {
    if (mainAxis == FLexDirectionRow) {
      return FLexDirectionRowReverse;
    } else if (mainAxis == FLexDirectionRowReverse) {
      return FLexDirectionRow;
    }
  }
  return mainAxis;
}

/* calculate cross axis by refer this node's flex properties
 * and layout direction which resolved in resolveDirection.
 * must use this method when cross axis alignment
 * to determine cross start & cross end
 */
FlexDirection HPNode::resolveCrossAxis() {
  FlexDirection mainAxis = style.flexDirection;
  FlexDirection crossAxis;
  //cross axis's direction rely on flex wrap mode.
  if (isRowDirection(mainAxis)) {
    if (style.flexWrap == FlexWrapReverse) {
      crossAxis = FLexDirectionColumnReverse;
    } else {
      crossAxis = FLexDirectionColumn;
    }
  } else {
    if (style.flexWrap == FlexWrapReverse) {
      crossAxis = FLexDirectionRowReverse;
    } else {
      crossAxis = FLexDirectionRow;
    }
    //in this situation crossAxis is Row Direction
    //should think about node's layout direction(HPDirection)
    HPDirection direction = getLayoutDirection();
    if (direction == DirectionRTL) {
      if (crossAxis == FLexDirectionRow) {
        crossAxis = FLexDirectionRowReverse;
      } else if (crossAxis == FLexDirectionRowReverse) {
        crossAxis = FLexDirectionRow;
      }
    }
  }
  return crossAxis;
}

FlexAlign HPNode::getNodeAlign(HPNodeRef item) {
  ASSERT(item != nullptr);
  if (item->style.alignSelf == FlexAlignAuto) {
    return style.alignItems;
  }
  return item->style.alignSelf;
}

float HPNode::boundAxis(FlexDirection axis, float value) {
  float min = style.minDim[axisDim[axis]];
  float max = style.maxDim[axisDim[axis]];
  float boundValue = value;
  if (!isUndefined(max) && max >= 0.0 && boundValue > max) {
    boundValue = max;
  }
  if (!isUndefined(min) && min >= 0.0 && boundValue < min) {
    boundValue = min;
  }
  return boundValue;
}

inline HPDirection HPNode::resolveDirection(HPDirection parentDirection) {
  return
      style.direction == DirectionInherit ?
          (parentDirection > DirectionInherit ? parentDirection : DirectionLTR) :
          style.direction;
}

//called after resolveDirection
void HPNode::resolveStyleValues() {
//  if (!isDirty) {
//    return;
//  }
  FlexDirection mainAxis = resolveMainAxis();
  FlexDirection crossAxis = resolveCrossAxis();
  //set layout margin value
  //auto margins are treated as zero. may be modified during layout process
  setLayoutStartMargin(mainAxis, getStartMargin(mainAxis));
  setLayoutEndMargin(mainAxis, getEndMargin(mainAxis));
  setLayoutStartMargin(crossAxis, getStartMargin(crossAxis));
  setLayoutEndMargin(crossAxis, getEndMargin(crossAxis));

  //set layout padding value
  result.padding[axisStart[mainAxis]] = style.getStartPadding(mainAxis);
  result.padding[axisEnd[mainAxis]] = style.getEndPadding(mainAxis);
  result.padding[axisStart[crossAxis]] = style.getStartPadding(crossAxis);
  result.padding[axisEnd[crossAxis]] = style.getEndPadding(crossAxis);

  //set layout border value;
  result.border[axisStart[mainAxis]] = style.getStartBorder(mainAxis);
  result.border[axisEnd[mainAxis]] = style.getEndBorder(mainAxis);
  result.border[axisStart[crossAxis]] = style.getStartBorder(crossAxis);
  result.border[axisEnd[crossAxis]] = style.getEndBorder(crossAxis);
}

#ifdef  LAYOUT_TIME_ANALYZE
static int layoutCount = 0;
static int layoutCacheCount = 0;
static int measureCount = 0;
static int measureCacheCount = 0;
#endif

void HPNode::layout(float parentWidth, float parentHeight,
                    HPDirection parentDirection,
                    void * layoutContext) {
#ifdef  LAYOUT_TIME_ANALYZE
  layoutCount = 0;
  layoutCacheCount = 0;
  measureCount = 0;
  measureCacheCount = 0;
#endif
  if (isUndefined(
      style.flexBasis) && !isUndefined(style.dim[axisDim[style.flexDirection]])) {
    style.flexBasis = style.dim[axisDim[style.flexDirection]];
  }

  //if container not set itself width and parent width is set,
  //set container width  as parentWidth subtract margin
  bool styleWidthReset = false;
  if (isUndefined(style.dim[DimWidth]) && isDefined(parentWidth)) {
    float containerWidth = parentWidth - getMargin(FLexDirectionRow);
    style.setDim(DimWidth, containerWidth > 0.0f ? containerWidth : 0.0f);
    styleWidthReset = true;
  }

  bool styleHeightReset = false;
  if (isUndefined(style.dim[DimHeight]) && isDefined(parentHeight)) {
    float containerHeight = parentHeight - getMargin(FLexDirectionColumn);
    style.setDim(DimHeight, containerHeight > 0.0f ? containerHeight : 0.0f);
    styleHeightReset = true;
  }
  layoutImpl(parentWidth, parentHeight, parentDirection, LayoutActionLayout, layoutContext);
  if (styleWidthReset) {
    style.setDim(DimWidth, VALUE_UNDEFINED);
  }
  if (styleHeightReset) {
    style.setDim(DimHeight, VALUE_UNDEFINED);
  }

  //calculate container's position
  FlexDirection mainAxis = resolveMainAxis();
  FlexDirection crossAxis = resolveCrossAxis();
  setLayoutStartPosition(mainAxis, getStartMargin(mainAxis), true);
  setLayoutEndPosition(mainAxis, getEndMargin(mainAxis), true);
  setLayoutStartPosition(crossAxis, getStartMargin(crossAxis), true);
  setLayoutEndPosition(crossAxis, getEndMargin(crossAxis), true);

  //node 's layout is complete
  //convert its and its descendants position and size to a integer value.
#ifndef ANDROID
  convertLayoutResult(0.0f, 0.0f);  //layout result convert has been taken in java . 3.8.2018. ianwang..
#endif

#ifdef  LAYOUT_TIME_ANALYZE
  HPLog(LogLevelDebug,"HippyLayoutTime layout: count %d cache %d, measure: count %d cache %d",
      layoutCount,layoutCacheCount,measureCount,measureCacheCount);
#endif
}

//3.Determine the flex base size and hypothetical main size of each item
void HPNode::calculateItemsFlexBasis(HPSize availableSize, void * layoutContext) {
  FlexDirection mainAxis = style.flexDirection;
  std::vector<HPNodeRef>& items = children;
  for (size_t i = 0; i < items.size(); i++) {
    HPNodeRef item = items[i];
    //for display none item, reset its and its descendants layout result.
    if (item->style.displayType == DisplayTypeNone) {
      item->resetLayoutRecursive();
      continue;
    }
    //https://stackoverflow.com/questions/34352140/what-are-the-differences-between-flex-basis-and-width
    //flex-basis has no effect on absolutely-positioned flex items. width and height properties
    //would be necessary.
    //Absolutely-positioned flex items do not participate in flex layout.
    if (item->style.positionType == PositionTypeAbsolute) {
      continue;
    }
    //3.Determine the flex base size and hypothetical main size of each item:
    //3.1 If the item has a definite used flex basis, that's the flex base size.
    if (isDefined(
        item->style.getFlexBasis()) && isDefined(style.dim[axisDim[mainAxis]])) {
      item->result.flexBaseSize = item->style.getFlexBasis();
    } else if (isDefined(item->style.dim[axisDim[mainAxis]])) {
      //flex-basis:auto:
      //When specified on a flex item, the auto keyword retrieves the value
      //of the main size property as the used flex-basis.
      //If that value is itself auto, then the used value is content.
      item->result.flexBaseSize = item->style.dim[axisDim[mainAxis]];
    } else {
      //3.2 Otherwise, size the item into the available space using its used flex basis
      //in place of its main size,
      float oldMainDim = item->style.getDim(mainAxis);
      //item->style.flexBasis is auto value
      item->style.setDim(mainAxis, item->style.flexBasis);
      item->layoutImpl(
          availableSize.width,
          availableSize.height,
          getLayoutDirection(),
          isRowDirection(mainAxis) ?
              LayoutActionMeasureWidth : LayoutActionMeasureHeight,
          layoutContext);
      item->style.setDim(mainAxis, oldMainDim);

      item->result.flexBaseSize =
      isDefined(item->result.dim[axisDim[mainAxis]]) ?
          item->result.dim[axisDim[mainAxis]] : 0;
    }

    //item->result.dim[axisDim[mainAxis]] = item->boundAxis(mainAxis, item->result.flexBasis);
    //The hypothetical main size is the item's flex base size clamped
    //according to its min and max main size properties (and flooring the content box size at zero).
    item->result.hypotheticalMainAxisSize = item->boundAxis(
        mainAxis, item->result.flexBaseSize);
    item->result.hypotheticalMainAxisMarginBoxSize = item->result
        .hypotheticalMainAxisSize + item->getMargin(mainAxis);
  }
}

bool HPNode::collectFlexLines(std::vector<FlexLine*>& flexLines,
                              HPSize availableSize) {
  std::vector<HPNodeRef>& items = children;
  bool sumHypotheticalMainSizeOverflow = false;
  float availableWidth =
      axisDim[style.flexDirection] == DimWidth ?
          availableSize.width : availableSize.height;
  if (isUndefined(availableWidth)) {
    availableWidth = INFINITY;
  }

  FlexLine * line = nullptr;
  int itemsSize = items.size();
  int i = 0;
  while (i < itemsSize) {
    HPNodeRef item = items[i];
    if (item->style.positionType == PositionTypeAbsolute
        || item->style.displayType == DisplayTypeNone) {
      //see HippyTest.dirty_mark_all_children_as_dirty_when_display_changes
      //when display changes.
      if (i == itemsSize - 1 && line != nullptr) {
        flexLines.push_back(line);
        break;
      }
      //
      i++;
      continue;
    }

    if (line == nullptr) {
      line = new FlexLine(this);
    }

    float leftSpace = availableWidth
        - (line->sumHypotheticalMainSize
            + item->result.hypotheticalMainAxisMarginBoxSize);
    if (leftSpace < 0) {
      //may be line wrap happened
      sumHypotheticalMainSizeOverflow = true;
    }

    if (style.flexWrap == FlexNoWrap) {
      line->addItem(item);
      if (i == itemsSize - 1) {
        flexLines.push_back(line);
        break;
      }
      i++;
    } else {
      if (leftSpace >= 0 || line->isEmpty()) {
        line->addItem(item);
        if (i == itemsSize - 1) {
          flexLines.push_back(line);
          line = nullptr;
        }
        i++;
      } else {

        flexLines.push_back(line);
        line = nullptr;
      }
    }
  }

  return sumHypotheticalMainSizeOverflow;
}

void HPNode::cacheLayoutOrMeasureResult(HPSize availableSize,
                                        HPSizeMode measureMode,
                                        FlexLayoutAction layoutAction) {

  HPSize resultSize = { result.dim[DimWidth], result.dim[DimHeight] };
  layoutCache.cacheResult(availableSize, resultSize, measureMode, layoutAction);
  if (layoutAction == LayoutActionLayout) {
    setDirty(false);
    setHasNewLayout(true);
    inInitailState = false;
  }
}

/*
 * availableWidth/availableHeight  has subtract its margin and padding.
 */
void HPNode::layoutSingleNode(float availableWidth,
                              MeasureMode widthMeasureMode,
                              float availableHeight,
                              MeasureMode heightMeasureMode,
                              FlexLayoutAction layoutAction,
                              void * layoutContext) {

  if (widthMeasureMode == MeasureModeExactly
      && heightMeasureMode == MeasureModeExactly) {
    result.dim[DimWidth] = availableWidth
        + getPaddingAndBorder(FLexDirectionRow);
    result.dim[DimHeight] = availableHeight
        + getPaddingAndBorder(FLexDirectionColumn);
  } else {
    //measure text, image etc. content node;
    HPSize dim = { 0, 0 };
    bool needMeasure = true;
    if (style.flexGrow > 0 && style.flexShrink > 0 && parent
        && parent->childCount() == 1
        && !parent->style.isDimensionAuto(FLexDirectionRow)
        && !parent->style.isDimensionAuto(FLexDirectionColumn)) {
      //don't measure single grow shrink child
      //see HPMeasureTest.cpp dont_measure_single_grow_shrink_child
      needMeasure = false;
    }

    if (!needMeasure) {
      dim.width = availableWidth;
      dim.height = availableHeight;
    } else if (measure != nullptr && needMeasure) {
      dim = measure(this, availableWidth, widthMeasureMode, availableHeight,
                    heightMeasureMode, layoutContext);
    }

    result.dim[DimWidth] = boundAxis(
        FLexDirectionRow,
        widthMeasureMode == MeasureModeExactly ?
            (availableWidth + getPaddingAndBorder(FLexDirectionRow)) :
            (dim.width + getPaddingAndBorder(FLexDirectionRow)));

    result.dim[DimHeight] = boundAxis(
        FLexDirectionColumn,
        heightMeasureMode == MeasureModeExactly ?
            (availableHeight + getPaddingAndBorder(FLexDirectionColumn)) :
            (dim.height + getPaddingAndBorder(FLexDirectionColumn)));
  }

  HPSize availableSize = { availableWidth, availableHeight };
  HPSizeMode measureMode = { widthMeasureMode, heightMeasureMode };
  cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
}

//reference: https://www.w3.org/TR/css-flexbox-1/#layout-algorithm
void HPNode::layoutImpl(float parentWidth, float parentHeight,
                        HPDirection parentDirection,
                        FlexLayoutAction layoutAction,
                        void * layoutContext) {
#ifdef  LAYOUT_TIME_ANALYZE
  if(layoutAction == LayoutActionLayout) {
    layoutCount++;
  } else {
    measureCount++;
  }
#endif

  HPDirection direction = resolveDirection(parentDirection);
  if (getLayoutDirection() != direction) {
    setLayoutDirection(direction);
    layoutCache.clearCache();
    resolveStyleValues();
  }

  FlexDirection mainAxis = style.flexDirection;
  bool performLayout = layoutAction == LayoutActionLayout;
  if (isDefined(parentWidth)) {
    parentWidth -= getMargin(FLexDirectionRow);
    parentWidth = parentWidth >= 0.0f ? parentWidth : 0.0f;
  }

  if (isDefined(parentHeight)) {
    parentHeight -= getMargin(FLexDirectionColumn);
    parentHeight = parentHeight >= 0.0f ? parentHeight : 0.0f;
  }

  //get node dim from style
  float nodeWidth =
  isDefined(style.dim[DimWidth]) ?
      boundAxis(FLexDirectionRow, style.dim[DimWidth]) : VALUE_UNDEFINED;

  float nodeHeight =
  isDefined(style.dim[DimHeight]) ?
      boundAxis(FLexDirectionColumn, style.dim[DimHeight]) :
      VALUE_UNDEFINED;

  //layoutMeasuredWidth  layoutMeasuredHeight used in
  //"Determine the flex base size and hypothetical main size of each item"
  if (layoutAction == LayoutActionMeasureWidth && isDefined(nodeWidth)) {
#ifdef  LAYOUT_TIME_ANALYZE
    measureCacheCount++;
#endif
    result.dim[DimWidth] = nodeWidth;
    return;
  } else if (layoutAction == LayoutActionMeasureHeight && isDefined(nodeHeight)) {
#ifdef  LAYOUT_TIME_ANALYZE
    measureCacheCount++;
#endif
    result.dim[DimHeight] = nodeHeight;
    return;
  }

  //9.2.Line Length Determination
  //Determine the available main and cross space for the flex items.
  //For each dimension, if that dimension of the flex container's content box is a definite size,
  //use that; if that dimension of the flex container is being sized under a min or max-content constraint,
  //the available space in that dimension is that constraint;
  //otherwise, subtract the flex container's margin, border, and padding
  //from the space available to the flex container in that dimension and use that value.
  //This might result in an infinite value.

  //get available size for layout or measure
  float availableWidth = VALUE_UNDEFINED;
  if (isDefined(nodeWidth)) {
    availableWidth = nodeWidth - getPaddingAndBorder(FLexDirectionRow);
  } else if (isDefined(parentWidth)) {
    availableWidth = parentWidth - getPaddingAndBorder(FLexDirectionRow);
  }

  float availableHeight = VALUE_UNDEFINED;
  if (isDefined(nodeHeight)) {
    availableHeight = nodeHeight - getPaddingAndBorder(FLexDirectionColumn);
  } else if (isDefined(parentHeight)) {
    availableHeight = parentHeight - getPaddingAndBorder(FLexDirectionColumn);
  }

  if (isDefined(style.maxDim[DimWidth])) {
    if (FloatIsEqual(style.maxDim[DimWidth], style.minDim[DimWidth])) {
      style.dim[DimWidth] = style.minDim[DimWidth];
    }
    float maxDimWidth = style.maxDim[DimWidth]
        - getPaddingAndBorder(FLexDirectionRow);
    if (maxDimWidth >= 0.0f && maxDimWidth < NanAsINF(availableWidth)) {
      availableWidth = maxDimWidth;
    }
  }

  if (isDefined(style.maxDim[DimHeight])) {
    if (FloatIsEqual(style.maxDim[DimHeight], style.minDim[DimHeight])) {
      style.dim[DimHeight] = style.minDim[DimHeight];
    }
    float maxDimHeight = style.maxDim[DimHeight]
        - getPaddingAndBorder(FLexDirectionColumn);
    if (maxDimHeight >= 0.0f && maxDimHeight < NanAsINF(availableHeight)) {
      availableHeight = maxDimHeight;
    }
  }

  //available size to layout...
  availableWidth = availableWidth < 0.0f ? 0.0f : availableWidth;
  availableHeight = availableHeight < 0.0f ? 0.0f : availableHeight;

  MeasureMode widthMeasureMode = MeasureModeUndefined;
  if (isDefined(style.dim[DimWidth])) {
    widthMeasureMode = MeasureModeExactly;
  } else if (isDefined(availableWidth)) {
    if (parent && parent->style.isOverflowScroll()
        && isRowDirection(parent->style.flexDirection)) {
      widthMeasureMode = MeasureModeUndefined;
      availableWidth = VALUE_AUTO;
    } else {
      widthMeasureMode = MeasureModeAtMost;
    }
  }

  MeasureMode heightMeasureMode = MeasureModeUndefined;
  if (isDefined(style.dim[DimHeight])) {
    heightMeasureMode = MeasureModeExactly;
  } else if (isDefined(availableHeight)) {
    if (parent && parent->style.isOverflowScroll()
        && isColumnDirection(parent->style.flexDirection)) {
      heightMeasureMode = MeasureModeUndefined;
      availableHeight = VALUE_AUTO;
    } else {
      heightMeasureMode = MeasureModeAtMost;
    }
  }

  HPSize availableSize = { availableWidth, availableHeight };
  HPSizeMode measureMode = { widthMeasureMode, heightMeasureMode };
  MeasureResult* cacheResult = layoutCache.getCachedMeasureResult(
      availableSize, measureMode, layoutAction, measure != nullptr);
  if (cacheResult != nullptr) {
    //set Result....
    switch (layoutAction) {
      case LayoutActionMeasureWidth:
#ifdef  LAYOUT_TIME_ANALYZE
        measureCacheCount++;
#endif
        ASSERT(isDefined(cacheResult->resultSize.width));
        result.dim[DimWidth] = cacheResult->resultSize.width;
        break;
      case LayoutActionMeasureHeight:
#ifdef  LAYOUT_TIME_ANALYZE
        measureCacheCount++;
#endif
        ASSERT(isDefined(cacheResult->resultSize.height));
        result.dim[DimHeight] = cacheResult->resultSize.height;
        break;
      case LayoutActionLayout:

        //if it's a measure node and cache result cached by LayoutActionMeasureWidth or LayoutActionMeasureHeight
        //so this is first layout for current Measure Node, set hasNewLayout as true,
        //if not, this node's layout result value has been fetched to java and set hasNewLayout false in FlexNode.cc
        //so not set hasNewLayout as true to avoid JNI call again in FLexNode.cc
        if (cacheResult->layoutAction
            != LayoutActionLayout&& measure != nullptr) {
          //need assign result size if layoutAction is different 3.14.2018
          result.dim[DimWidth] = cacheResult->resultSize.width;
          result.dim[DimHeight] = cacheResult->resultSize.height;
          cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
        } else {
#ifdef  LAYOUT_TIME_ANALYZE
          layoutCacheCount++;
#endif
          //do nothing..
          //layoutCache.cachedLayout object is last layout result.
          //used to determine need layout or not.
        }

        //if it's a measure node , layout could be cache by LayoutActionMeasureWidth
        //or LayoutActionMeasureHeight,so in this case, we need set dirty as false;
        setDirty(false);

        break;
      default:
        break;
    }
    return;
  }
  //before layout set result's hadOverflow as false.
  if (layoutAction == LayoutActionLayout) {
    result.hadOverflow = false;
  }
  //single element measure width and height
  if ((children.size() == 0)) {
    layoutSingleNode(availableWidth, widthMeasureMode, availableHeight,
                     heightMeasureMode, layoutAction, layoutContext);
    return;
  }
  //3.Determine the flex base size and hypothetical main size of each item
  calculateItemsFlexBasis(availableSize, layoutContext);
  //9.3. Main Size Determination
  // 5. Collect flex items into flex lines:
  std::vector<FlexLine *> flexLines;
  bool sumHypotheticalMainSizeOverflow = collectFlexLines(flexLines,
                                                          availableSize);

  //get max line's  main size
  float maxSumItemsMainSize = 0;
  for (size_t i = 0; i < flexLines.size(); i++) {
    if (flexLines[i]->sumHypotheticalMainSize > maxSumItemsMainSize) {
      maxSumItemsMainSize = flexLines[i]->sumHypotheticalMainSize;
    }
  }

  // 4. Determine the main size of the flex container using the rules of the formatting context
  //in which it participates. For this computation, auto margins on flex items are treated as 0.
  //TODO:: if has set , what to do for next run in determineCrossAxisSize's  layoutImpl
  float containerInnerMainSize = 0.0f;
  if (isDefined(style.dim[axisDim[mainAxis]])) {
    //MeasureModeExactly
    containerInnerMainSize = style.dim[axisDim[mainAxis]]
        - getPaddingAndBorder(mainAxis);
  } else {
    if (sumHypotheticalMainSizeOverflow) {	//MeasureModeAtMost
      //if sum of hypothetical MainSize > available size;
      float mainInnerSize =
          axisDim[mainAxis] == DimWidth ?
              availableSize.width : availableSize.height;

      if (maxSumItemsMainSize > mainInnerSize && !style.isOverflowScroll()) {
        if (parent && parent->getNodeAlign(this) == FlexAlignStretch
            && axisDim[mainAxis] == axisDim[parent->resolveCrossAxis()]
            && style.positionType != PositionTypeAbsolute) {
          //it this node has text child and node main axis(width) is stretch
          //,cross axis length(height) is undefined
          //text can has multi-line, text's height can affect parent's height
          //in this situation, use availableSize if possible
          containerInnerMainSize = mainInnerSize;
        } else {
          containerInnerMainSize = maxSumItemsMainSize;
        }
      } else {
        containerInnerMainSize = mainInnerSize;
      }
    } else {
      containerInnerMainSize = maxSumItemsMainSize;
    }
  }
  result.dim[axisDim[mainAxis]] = boundAxis(
      mainAxis, containerInnerMainSize + getPaddingAndBorder(mainAxis));
  //return if its just in measure
  if ((layoutAction == LayoutActionMeasureWidth && isRowDirection(mainAxis))
      || (layoutAction == LayoutActionMeasureHeight
          && isColumnDirection(mainAxis))) {
    //cache layout result & state...
    cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
    //free flexLines, allocate in collectFlexLines.
    //TODO:: opt.
    for (size_t i = 0; i < flexLines.size(); i++) {
      delete flexLines[i];
    }
    return;
  }

  //6. Resolving Flexible Lengths
  //To resolve the flexible lengths of the items within a flex line:
  //TODO:://this's the only place that confirm child items main axis size, see item->setLayoutDim
  determineItemsMainAxisSize(flexLines, layoutAction);

  //9.4. Cross Size Determination
  //calculate line's cross size in flexLines
  //TODO:: The real place that Determine
  //the flex container's used cross size is at step 15.

  float sumLinesCrossSize = determineCrossAxisSize(flexLines, availableSize,
                                                   layoutAction, layoutContext);

  if (!performLayout) {
    //TODO::for measure, I put the calculate of flex container's cross size in here..
    //TODO::why must in step 15 in W3 flex layout algorithm
    //noted by ianwang 12.30.2017.

    //15.Determine the flex container's used cross size:
    //If the cross size property is a definite size, use that,
    //clamped by the min and max cross size properties of the flex container.
    //Otherwise, use the sum of the flex lines' cross sizes,
    //clamped by the min and max cross size properties of the flex container.
    FlexDirection crossAxis = resolveCrossAxis();
    float crossDimSize;
    if (isDefined(style.dim[axisDim[crossAxis]])) {
      crossDimSize = style.dim[axisDim[crossAxis]];
    } else {
      crossDimSize = (sumLinesCrossSize + getPaddingAndBorder(crossAxis));
    }
    result.dim[axisDim[crossAxis]] = boundAxis(crossAxis, crossDimSize);
    //cache layout result & state...
    cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);

    //free flexLines, allocate in collectFlexLines.
    for (size_t i = 0; i < flexLines.size(); i++) {
      delete flexLines[i];
    }
    return;
  }

  //9.5. Main-Axis Alignment
  mainAxisAlignment(flexLines);

  //9.6. Cross-Axis Alignment
  //if contianer's innerCross size not defined,
  //then it will be determined in step 15 of crossAxisAlignment
  crossAxisAlignment(flexLines);

  //free flexLines, allocate in collectFlexLines.
  for (size_t i = 0; i < flexLines.size(); i++) {
    delete flexLines[i];
  }

  //cache layout result & state...
  cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
  //layout fixed elements...
  layoutFixedItems(measureMode, layoutContext);

  return;
}

//9.4. Cross Size Determination
float HPNode::determineCrossAxisSize(std::vector<FlexLine*>& flexLines,
                                     HPSize availableSize,
                                     FlexLayoutAction layoutAction,
                                     void * layoutContext) {
  FlexDirection mainAxis = style.flexDirection;
  FlexDirection crossAxis = resolveCrossAxis();
  float sumLinesCrossSize = 0;
  for (size_t i = 0; i < flexLines.size(); i++) {
    FlexLine * line = flexLines[i];
    float maxItemCrossSize = 0;
    for (size_t j = 0; j < line->items.size(); j++) {
      HPNodeRef item = line->items[j];
      //item's main axis size has been determined.
      //try to calculate the hypothetical cross size of each item
      //that would be stored in result.dim[crossAxis]
      //align stretch may be modify this value in the later step.

      //WARNING TODO::this is the only place that the Recursive flex layout happen.
      //7.Determine the hypothetical cross size of each item by performing layout with
      //the used main size and the available space, treating auto as fit-content.
      FlexLayoutAction oldLayoutAction = layoutAction;
      if (getNodeAlign(item) == FlexAlignStretch
          && item->style.isDimensionAuto(crossAxis)
          && !item->style.hasAutoMargin(crossAxis)
          && layoutAction == LayoutActionLayout) {
        //Delay layout for stretch item, do layout later in step 11.
        layoutAction =
            axisDim[crossAxis] == DimWidth ?
                LayoutActionMeasureWidth : LayoutActionMeasureHeight;
      }
      float oldMainDim = item->style.getDim(mainAxis);
      item->style.setDim(mainAxis, item->getLayoutDim(mainAxis));
      item->layoutImpl(availableSize.width, availableSize.height,
                       getLayoutDirection(), layoutAction, layoutContext);
      item->style.setDim(mainAxis, oldMainDim);
      layoutAction = oldLayoutAction;
      //if child item had overflow , then transfer this state to its parent.
      //see HippyTest_HadOverflowTests.spacing_overflow_in_nested_nodes in ./tests/HPHadOverflowTest.cpp
      result.hadOverflow = result.hadOverflow | item->result.hadOverflow;

      //TODO:: if need support baseline  add here
      //8.Calculate the cross size of each flex line.
      //1)Collect all the flex items whose inline-axis is parallel to the main-axis,
      //whose align-self is baseline, and whose cross-axis margins are both non-auto.
      //Find the largest of the distances between each item's baseline and its hypothetical
      //outer cross-start edge, and the largest of the distances between each item's baseline
      //and its hypothetical outer cross-end edge, and sum these two values.
      //2)Among all the items not collected by the previous step, find the largest outer
      //hypothetical cross size.
      //3)The used cross-size of the flex line is the largest of the numbers found
      //in the previous two steps and zero.

      //Max item cross size
      float itemOutCrossSize = item->getLayoutDim(crossAxis)
          + item->getMargin(crossAxis);
      if (itemOutCrossSize > maxItemCrossSize) {
        maxItemCrossSize = itemOutCrossSize;
      }
    }

    //8.Calculate the cross size of each flex line.
    //clip current container cross axis size..
    maxItemCrossSize = boundAxis(crossAxis, maxItemCrossSize);
    line->lineCrossSize = maxItemCrossSize;
    sumLinesCrossSize += maxItemCrossSize;

    //single line , set line height as container inner height
    if (flexLines.size() == 1 && isDefined(style.dim[axisDim[crossAxis]])) {
      //if following assert is true, means front-end's style is in unsuitable state ..
      //such as main axis is undefined but set flex-wrap as FlexWrap.
      //ASSERT(style.flexWrap == FlexNoWrap);
      float innerCrossSize = boundAxis(crossAxis, style.dim[axisDim[crossAxis]])
          - getPaddingAndBorder(crossAxis);

      line->lineCrossSize = innerCrossSize;
      sumLinesCrossSize = innerCrossSize;
    }
  }

  //9.Handle 'align-content: stretch' for lines
  if (isDefined(style.dim[axisDim[crossAxis]])
      && style.alignContent == FlexAlignStretch) {

    float innerCrossSize = boundAxis(crossAxis, style.dim[axisDim[crossAxis]])
        - getPaddingAndBorder(crossAxis);
    if (sumLinesCrossSize < innerCrossSize) {
      for (size_t i = 0; i < flexLines.size(); i++) {
        FlexLine * line = flexLines[i];
        line->lineCrossSize += (innerCrossSize - sumLinesCrossSize)
            / flexLines.size();
      }
    }
  }

  //11.Determine the used cross size of each flex item
  //Think about item align-self: stretch
  for (size_t i = 0; i < flexLines.size(); i++) {
    FlexLine * line = flexLines[i];
    for (size_t j = 0; j < line->items.size(); j++) {
      HPNodeRef item = line->items[j];

      //1): If a flex item has align-self: stretch, its computed cross size property is auto,
      //    and neither of its cross-axis margins are auto, the used outer cross size is the used
      //    cross size of its flex line, clamped according to the item's
      //    min and max cross size properties.
      //2):Otherwise,the used cross size is the item's hypothetical cross size.
      if (getNodeAlign(item) == FlexAlignStretch
          && item->style.isDimensionAuto(crossAxis)
          && !item->style.hasAutoMargin(crossAxis)) {

        item->result.dim[axisDim[crossAxis]] = item->boundAxis(
            crossAxis, line->lineCrossSize - item->getMargin(crossAxis));
        //If the flex item has align-self: stretch, redo layout for its contents,
        //treating this used size as its definite cross size
        //so that percentage-sized children can be resolved.
        float oldMainDim = item->style.getDim(mainAxis);
        float oldCrossDim = item->style.getDim(crossAxis);
        item->style.setDim(mainAxis, item->getLayoutDim(mainAxis));
        item->style.setDim(crossAxis, item->getLayoutDim(crossAxis));
        item->layoutImpl(availableSize.width, availableSize.height,
                         getLayoutDirection(), layoutAction, layoutContext);
        item->style.setDim(mainAxis, oldMainDim);
        item->style.setDim(crossAxis, oldCrossDim);

      } else {
        //Otherwise, the used cross size is the item's hypothetical cross size.
        //see the step7.
        //item's hypothetical cross size. has been set in result.dim[axisDim[crossAxis]].
      }
    }
  }

  //TODO::Why Determine  the flex container's used cross size in step 15.
  return sumLinesCrossSize;
}

//See  9.7 Resolving Flexible Lengths.
void HPNode::determineItemsMainAxisSize(std::vector<FlexLine *> & flexLines,
                                        FlexLayoutAction layoutAction) {
  FlexDirection mainAxis = style.flexDirection;
  float mainAxisContentSize = result.dim[axisDim[mainAxis]]
      - getPaddingAndBorder(mainAxis);
  // 6. Resolve the flexible lengths of all the flex items to find their used main size (see section 9.7.)
  for (size_t i = 0; i < flexLines.size(); i++) {
    FlexLine * line = flexLines[i];
    line->SetContainerMainInnerSize(mainAxisContentSize);
    line->FreezeInflexibleItems(layoutAction);
    while (!line->ResolveFlexibleLengths()) {
      ASSERT(line->totalFlexGrow >= 0);
      ASSERT(line->totalFlexGrow >= 0);
    }

    if (layoutAction == LayoutActionLayout && line->remainingFreeSpace < 0) {
      result.hadOverflow = true;
    }
  }
}

//9.5 Main-Axis Alignment
void HPNode::mainAxisAlignment(std::vector<FlexLine *> & flexLines) {
  //TODO:: RTL::
  // 12. Distribute any remaining free space. For each flex line:
  FlexDirection mainAxis = style.flexDirection;
  float mainAxisContentSize = getLayoutDim(mainAxis)
      - getPaddingAndBorder(mainAxis);
  for (size_t i = 0; i < flexLines.size(); i++) {
    FlexLine * line = flexLines[i];
    line->SetContainerMainInnerSize(mainAxisContentSize);
    line->alignItems();
  }
}

//9.6 Cross-Axis Alignment
void HPNode::crossAxisAlignment(std::vector<FlexLine *> & flexLines) {
  FlexDirection crossAxis = resolveCrossAxis();
  float sumLinesCrossSize = 0;
  int linesCount = flexLines.size();
  for (int i = 0; i < linesCount; i++) {
    FlexLine * line = flexLines[i];
    sumLinesCrossSize += line->lineCrossSize;
    for (size_t j = 0; j < line->items.size(); j++) {
      HPNodeRef item = line->items[j];
      //13.Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
      float remainingFreeSpace = line->lineCrossSize
          - item->result.dim[axisDim[crossAxis]] - item->getMargin(crossAxis);
      if (remainingFreeSpace > 0) {
        //If its outer cross size (treating those auto margins as zero) is less than
        //the cross size of its flex line, distribute the difference
        //in those sizes equally to the auto margins.
        if (item->isAutoStartMargin(crossAxis)
            && item->isAutoEndMargin(crossAxis)) {
          item->setLayoutStartMargin(crossAxis, remainingFreeSpace / 2);
          item->setLayoutEndMargin(crossAxis, remainingFreeSpace / 2);
        } else if (item->isAutoStartMargin(crossAxis)) {
          item->setLayoutStartMargin(crossAxis, remainingFreeSpace);
        } else if (item->isAutoEndMargin(crossAxis)) {
          item->setLayoutEndMargin(crossAxis, remainingFreeSpace);
        } else {
          //For margin:: assign style value to result value at this place..
          item->setLayoutStartMargin(crossAxis,
                                     item->getStartMargin(crossAxis));
          item->setLayoutEndMargin(crossAxis, item->getEndMargin(crossAxis));
        }
      } else {
        //Otherwise, if the block-start or inline-start margin
        //(whichever is in the cross axis) is auto, set it to zero.
        //Set the opposite margin so that the outer cross size of the
        //item equals the cross size of its flex line.
        item->setLayoutStartMargin(crossAxis, item->getStartMargin(crossAxis));
        item->setLayoutEndMargin(crossAxis, item->getEndMargin(crossAxis));
      }

      //14.Align all flex items along the cross-axis per align-self,
      //if neither of the item's cross-axis margins are auto.
      //calculate item's offset in its line by style align-self
      remainingFreeSpace = line->lineCrossSize
          - item->result.dim[axisDim[crossAxis]]
          - item->getLayoutMargin(crossAxis);
      float offset = item->getLayoutStartMargin(crossAxis);
      switch (getNodeAlign(item)) {  //when align self is auto , it overwrite by align items
        case FlexAlignStart:
          break;
        case FlexAlignCenter:
          offset += remainingFreeSpace / 2;
          break;
        case FlexAlignEnd:
          offset += remainingFreeSpace;
          break;
          //TODO:: case baseline alignment
        default:
          break;
      }
      //include (axisStart[crossAxis] == CSSTop) and (axisStart[crossAxis] == CSSBottom)
      //For temporary store. use false parameter
      item->setLayoutStartPosition(crossAxis, offset, false);
    }
  }

  //15.Determine  the flex container's used cross size:
  //If the cross size property is a definite size, use that,
  //clamped by the min and max cross size properties of the flex container.
  //Otherwise, use the sum of the flex lines' cross sizes,
  //clamped by the min and max cross size properties of the flex container.

  float crossDimSize;
  if (isDefined(style.dim[axisDim[crossAxis]])) {
    crossDimSize = style.dim[axisDim[crossAxis]];
  } else {
    crossDimSize = (sumLinesCrossSize + getPaddingAndBorder(crossAxis));
  }
  result.dim[axisDim[crossAxis]] = boundAxis(crossAxis, crossDimSize);

  //when container's cross size determined align all flex lines by align-content
  //16.Align all flex lines per align-content
  float innerCrossSize = result.dim[axisDim[crossAxis]]
      - getPaddingAndBorder(crossAxis);
  float remainingFreeSpace = innerCrossSize - sumLinesCrossSize;
  float offset = getStartPaddingAndBorder(crossAxis);
  float space = 0;
  switch (style.alignContent) {
    case FlexAlignStart:
      break;
    case FlexAlignCenter:
      offset += remainingFreeSpace / 2;
      break;
    case FlexAlignEnd:
      offset += remainingFreeSpace;
      break;
    case FlexAlignSpaceBetween:
      space = remainingFreeSpace / (linesCount - 1);
      break;
    case FlexAlignSpaceAround:
      space = remainingFreeSpace / linesCount;
      offset += space / 2;
      break;
    default:
      break;
  }

  //flex-end::
  //The cross-end margin edge of the flex item is placed flush with the cross-end edge of the line.
  //crossAxisPostionStart calculated along the cross axis direction.
  float crossAxisPostionStart = offset;
  for (int i = 0; i < linesCount; i++) {
    FlexLine * line = flexLines[i];
    for (size_t j = 0; j < line->items.size(); j++) {
      HPNodeRef item = line->items[j];
      //include (axisStart[crossAxis] == CSSTop) and (axisStart[crossAxis] == CSSBottom)
      //getLayoutStartPosition set in step 14.
      item->setLayoutStartPosition(
          crossAxis,
          crossAxisPostionStart + item->getLayoutStartPosition(crossAxis));
      //layout start position has use relative ,so end position not use it ,use false parameter.
      item->setLayoutEndPosition(
          crossAxis,
          (getLayoutDim(crossAxis) - item->getLayoutStartPosition(crossAxis)
              - item->getLayoutDim(crossAxis)),
          false);

    }

    crossAxisPostionStart += line->lineCrossSize + space;
  }
}

//4.1. Absolutely-Positioned Flex Children
//As it is out-of-flow, an absolutely-positioned child of a flex container
//does not participate in flex layout.
//The static position of an absolutely-positioned child of a flex container
//is determined such that the child is positioned as if it were the sole flex item in the flex container,
//assuming both the child and the flex container were fixed-size boxes of their used size.
//For this purpose, auto margins are treated as zero.
void HPNode::layoutFixedItems(HPSizeMode measureMode, void * layoutContext) {
  FlexDirection mainAxis = resolveMainAxis();
  FlexDirection crossAxis = resolveCrossAxis();
  std::vector<HPNodeRef>& items = children;
  for (size_t i = 0; i < items.size(); i++) {
    HPNodeRef item = items[i];
    //for display none item, reset its layout result.
    if (item->style.displayType == DisplayTypeNone) {
      item->resetLayoutRecursive();
      continue;
    }
    if (item->style.positionType != PositionTypeAbsolute) {
      continue;
    }

    float parentWidth = getLayoutDim(FLexDirectionRow)
        - getPaddingAndBorder(FLexDirectionRow);
    float parentHeight = getLayoutDim(FLexDirectionColumn)
        - getPaddingAndBorder(FLexDirectionColumn);

    float itemOldStyleDimMainAxis = item->style.getDim(mainAxis);
    float itemOldStyleDimCrossAxis = item->style.getDim(crossAxis);

    if (isUndefined(itemOldStyleDimMainAxis)
        && isDefined(item->style.getStartPosition(mainAxis))
        && isDefined(item->style.getEndPosition(mainAxis))) {
        item->style.setDim(
            mainAxis,
            (getLayoutDim(mainAxis) - style.getStartBorder(mainAxis)
                - style.getEndBorder(mainAxis)
                - item->style.getStartPosition(mainAxis)
                - item->style.getEndPosition(mainAxis)
                - item->getMargin(mainAxis)));
    }

    if (isUndefined(itemOldStyleDimCrossAxis)
        && isDefined(item->style.getStartPosition(crossAxis))
        && isDefined(item->style.getEndPosition(crossAxis))) {
        item->style.setDim(
            crossAxis,
            (getLayoutDim(crossAxis) - style.getStartBorder(crossAxis)
                - style.getEndBorder(crossAxis)
                - item->style.getStartPosition(crossAxis)
                - item->style.getEndPosition(crossAxis)
                - item->getMargin(crossAxis)));
    }


    item->layoutImpl(parentWidth, parentHeight, getLayoutDirection(),
                     LayoutActionLayout, layoutContext);
    //recover item's previous style value
    item->style.setDim(mainAxis, itemOldStyleDimMainAxis);
    item->style.setDim(crossAxis, itemOldStyleDimCrossAxis);
    //after layout, calculate fix item 's postion
    //1) for main axis
    calculateFixedItemPosition(item, mainAxis);
    //2)for cross axis
    calculateFixedItemPosition(item, crossAxis);
  }
}

//when item's layout complete, update fixed item's position on Specified axis
//called in layoutFixedItems
//should be called twice, one for main axis ,one for cross axis
void HPNode::calculateFixedItemPosition(HPNodeRef item, FlexDirection axis) {
  if (isDefined(item->style.getStartPosition(axis))) {
    item->setLayoutStartPosition(
        axis,
        getStartBorder(axis) + item->getLayoutStartMargin(axis)
            + item->style.getStartPosition(axis));
    item->setLayoutEndPosition(
        axis,
        getLayoutDim(axis) - item->getLayoutStartPosition(axis)
            - item->getLayoutDim(axis));

  } else if (isDefined(item->style.getEndPosition(axis))) {
    item->setLayoutEndPosition(
        axis,
        getEndBorder(axis) + item->getLayoutEndMargin(axis)
            + item->style.getEndPosition(axis));
    item->setLayoutStartPosition(
        axis,
        getLayoutDim(axis) - item->getLayoutEndPosition(axis)
            - item->getLayoutDim(axis));
  } else {
    float remainingFreeSpace = getLayoutDim(axis) - getPaddingAndBorder(axis)
        - item->getLayoutDim(axis);
    float offset = getStartPaddingAndBorder(axis);
    FlexAlign alignMode =
        (axis == resolveMainAxis() ? style.justifyContent : getNodeAlign(item));
    switch (alignMode) {
      case FlexAlignStart:
        break;
      case FlexAlignCenter:
        offset += remainingFreeSpace / 2;
        break;
      case FlexAlignEnd:
        offset += remainingFreeSpace;
        break;
      default:
        break;
    }

    item->setLayoutStartPosition(
        axis,
        getStartPaddingAndBorder(axis) + item->getLayoutStartMargin(axis)
            + offset);
    item->setLayoutEndPosition(
        axis,
        getLayoutDim(axis) - item->getLayoutStartPosition(axis)
            - item->getLayoutDim(axis));
  }
}


//convert position and dimension values to integer value..
//absLeft, absTop is mainly think about the influence of parent's Fraction offset
//for example: if parent's Fraction offset is 0.3
//and current child offset is 0.4
//then the child's absolute offset  is 0.7.
//if use roundf , roundf(0.7) == 1
//so we need absLeft, absTop  parameter
void HPNode::convertLayoutResult(float absLeft, float absTop) {
  if (!hasNewLayout()) {
    return;
  }
  const float left = result.position[CSSLeft];
  const float top = result.position[CSSTop];
  const float width = result.dim[DimWidth];
  const float height = result.dim[DimHeight];

  absLeft += left;
  absTop += top;
  bool isTextNode = style.nodeType == NodeTypeText;
  result.position[CSSLeft] = HPRoundValueToPixelGrid(left, false, isTextNode);
  result.position[CSSTop] = HPRoundValueToPixelGrid(top, false, isTextNode);

  const bool hasFractionalWidth = !FloatIsEqual(fmodf(width, 1.0), 0)
      && !FloatIsEqual(fmodf(width, 1.0), 1.0);
  const bool hasFractionalHeight = !FloatIsEqual(fmodf(height, 1.0), 0)
      && !FloatIsEqual(fmodf(height, 1.0), 1.0);

  const float absRight = absLeft + width;
  const float absBottom = absTop + height;
  result.dim[DimWidth] = HPRoundValueToPixelGrid(
      absRight, (isTextNode && hasFractionalWidth),
      (isTextNode && !hasFractionalWidth))
      - HPRoundValueToPixelGrid(absLeft, false, isTextNode);

  result.dim[DimHeight] = HPRoundValueToPixelGrid(
      absBottom, (isTextNode && hasFractionalHeight),
      (isTextNode && !hasFractionalHeight))
      - HPRoundValueToPixelGrid(absTop, false, isTextNode);
  std::vector<HPNodeRef>& items = children;
  for (size_t i = 0; i < items.size(); i++) {
    HPNodeRef item = items[i];
    item->convertLayoutResult(absLeft, absTop);
  }
}

